#ifdef __cplusplus
extern "C" {
#endif


#ifndef MY_TEST_ECAT_COMMON_H
#define MY_TEST_ECAT_COMMON_H

#include <sys/time.h>
#include <time.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "slave_config.h"
#include "ecrt.h"
#include "ec_common_configs_define.h"

/****************************************************************************/

typedef enum EcCommonErr{ EC_COMMON_OK=0, EC_COMMON_ERR_REUEST_MASTER, EC_COMMON_ERR_ACTIVATE_MASTER, EC_COMMON_ERR_NOT_INIT_MASTER, EC_COMMON_ERR_REUEST_DOMAIN, EC_COMMON_ERR_REUEST_DOMAIN_PD, 
						   EC_COMMON_ERR_REUEST_ALLOC_SLAVE_CONFIGS, EC_COMMON_ERR_CFG_SLAVES, EC_COMMON_ERR_NO_SLAVE, EC_COMMON_ERR_NO_SLAVE_INDEX, EC_COMMON_ERR_INVALID_VALUE,
						   EC_COMMON_ERR_INVALID_DATATYPE} EcCommonErr_T;

typedef enum EcCommonDataType {EC_COMMON_DATA_TYPE_BIT, EC_COMMON_DATA_TYPE_U8,  EC_COMMON_DATA_TYPE_S8, EC_COMMON_DATA_TYPE_U16, EC_COMMON_DATA_TYPE_S16, EC_COMMON_DATA_TYPE_U32,
								 EC_COMMON_DATA_TYPE_S32, EC_COMMON_DATA_TYPE_U64, EC_COMMON_DATA_TYPE_S64}EcCommonDataType_T;


/****************************************************************************/
#define EC_AL_STATE_BOOT 0x03
#define AL_STATE_STR(x) (x == EC_AL_STATE_INIT   ? "AL_STATE_INIT"     :\
					     x == EC_AL_STATE_PREOP  ? "AL_STATE_PREOP"    :\
					     x == EC_AL_STATE_SAFEOP ? "AL_STATE_SAFEOP"   :\
					     x == EC_AL_STATE_OP     ? "AL_STATE_OP"       :\
					     x == EC_AL_STATE_BOOT   ? "AL_STATE_BOOT"     : "AL_STATE_UNKNOWN")
					  
#define DOMAIN_STATE_STR(x) (x == EC_WC_ZERO       ? "DOMAIN_STATE_WC_ZERO"       : \
	                         x == EC_WC_INCOMPLETE ? "DOMAIN_STATE_WC_INCOMPLETE" : \
	                         x == EC_WC_COMPLETE   ? "DOMAIN_STATE_WC_COMPLETE"   : "DOMAIN_STATE_UNKNOWN")
	                         
/****************************************************************************/

#define RECEIVE_PDO_DATA(master, domain) do {ecrt_master_receive(master);ecrt_domain_process(domain);} while(0)
#define SEND_PDO_DATA(master, domain) do {ecrt_domain_queue(domain);ecrt_master_send(master);}while(0)


#define EC_COMMON_STRCAT(A, B, C) (A##B##C)
#define LAST_DATA_IN_DOMAIN_REGS(X) (((0 == X.index) && (0 == X.subindex))) 
#define LAST_DATA_IN_SLAVES_SPECIFIED_INFOS(X) (((-1 == X.slave_id_info.alias) && (-1 == X.slave_id_info.position) \
												 && (-1 == X.slave_id_info.vendorid) && (-1 == X.slave_id_info.productcode)))

//Generate array of pdo_entry_reg_t for all slaves attatched to master_specified_info								 
#define  EC_COMMON_GENERATE_MASTER_SLAVES_PDO_ENTRY_REG_ARR(master_specified_info) \
	do \
	{ \
		int i = 0; \
		while (!LAST_DATA_IN_SLAVES_SPECIFIED_INFOS(master_specified_info->slaves_specified_infos[i]))  \
		{ \
			master_specified_info->slaves_specified_infos[i].domain_regs =  ec_common_generate_pdo_entry_reg_arr(master_specified_info->slaves_specified_infos[i].slave_id_info.alias, master_specified_info->slaves_specified_infos[i].slave_id_info.position, \
																			master_specified_info->slaves_specified_infos[i].slave_id_info.vendorid, master_specified_info->slaves_specified_infos[i].slave_id_info.productcode, \
																			master_specified_info->slaves_specified_infos[i].pdo_entries, master_specified_info->slaves_specified_infos[i].pdo_entries_size); \
			if (NULL == master_specified_info->slaves_specified_infos[i].domain_regs)\
			{\
				i--; \
				while (i>=0) \
				{ \
					ec_common_free_pdo_entry_reg_arr(master_specified_info->slaves_specified_infos[i].domain_regs);\
				}\
				return EC_COMMON_ERR_CFG_SLAVES; \
			} \
			i++;	\
		}\
	}while(0)

//To free malloc space allocated by master
#define EC_COMMON_FREE_MASTER_SPACE(master_specified_info) \
	do \
	{\
		int j;\
		if (!master_specified_info->slaves_config)\
			free(master_specified_info->slaves_config); \
		j = 0; \
		while (!LAST_DATA_IN_SLAVES_SPECIFIED_INFOS(master_specified_info->slaves_specified_infos[j]))	\
		{ \
			if(!master_specified_info->slaves_specified_infos[j].domain_regs)\
				ec_common_free_pdo_entry_reg_arr(master_specified_info->slaves_specified_infos[j].domain_regs); \
			j++; \
		}\
	}while(0)


//Get array of ec_slave_config_t for all slaves attached to master
#define EC_COMMON_CFG_MASTER_SLAVES(master_specified_info) \
	do\
	{\
	    int i=0;\
		ec_slave_config_t *tmp_ec_slave_config = NULL;\
		EcCommonErr_T err_code = EC_COMMON_OK;\
		for (i=0; i<master_specified_info->slaves_count; i++) \
		{\
			tmp_ec_slave_config = ecrt_master_slave_config(master_specified_info->master, master_specified_info->slaves_specified_infos[i].slave_id_info.alias, master_specified_info->slaves_specified_infos[i].slave_id_info.position,\
															master_specified_info->slaves_specified_infos[i].slave_id_info.vendorid, master_specified_info->slaves_specified_infos[i].slave_id_info.productcode);\
			if (NULL == tmp_ec_slave_config)\
			{\
				err_code = EC_COMMON_ERR_CFG_SLAVES;\
				EC_COMMON_FREE_MASTER_SPACE(master_specified_info);\
				return err_code;\
			}\
			else\
			{\
				master_specified_info->slaves_config[i] = *tmp_ec_slave_config;\
				err_code = ecrt_slave_config_pdos(&master_specified_info->slaves_config[i], master_specified_info->slaves_specified_infos[i].syns_size, master_specified_info->slaves_specified_infos[i].syncs);\
				if (err_code)\
				{\
					err_code = EC_COMMON_ERR_CFG_SLAVES;\
					EC_COMMON_FREE_MASTER_SPACE(master_specified_info);\
					return err_code;\
				}\
				err_code = ecrt_domain_reg_pdo_entry_list(master_specified_info->domain, master_specified_info->slaves_specified_infos[i].domain_regs);\
				if (err_code)\
				{\
					err_code = EC_COMMON_ERR_CFG_SLAVES;\
					EC_COMMON_FREE_MASTER_SPACE(master_specified_info);\
					return err_code;\
				}\
			}\
		}\
	}while(0)



/*****************************SelfPrintf***********************************************/
#define PRINTF_LEVEL 0
#if (PRINTF_LEVEL == 0)
#define EC_COMMON_LOG(...) printf("EC_COMMON_DEBUG=======:"__VA_ARGS__)
#define EC_COMMON_LOG_ERROR(err_msg) printf("EC_COMMON_DEBUG_ERROR=======: %s:%s:%d", __FILE__, __FUNCTION__, __LINE__);printf(" (err_msg:%s)\n", err_msg)
#elif  (PRINTF_LEVEL == 1)
#define EC_COMMON_LOG(...) printf(__VA_ARGS__)
#define EC_COMMON_LOG_ERROR(err_msg) printf("ERROR=======: %s:%s:%d", __FILE__, __FUNCTION__, __LINE__);printf(" (err_msg:%s)\n", err_msg)
#else
#define EC_COMMON_LOG(...) do {} while(0)
#define EC_COMMON_LOG_ERROR(err_msg) do {} while(0)
#endif

/****************************************************************************/
// Application parameters
#define FREQUENCY 1000
#define CLOCK_TO_USE CLOCK_REALTIME
#define MEASURE_TIMING 1
#define PRIORITY 1

/****************************************************************************/
#define NSEC_PER_SEC (1000000000L)
#define PERIOD_NS (NSEC_PER_SEC / FREQUENCY)
#define DIFF_NS(A, B) (((B).tv_sec - (A).tv_sec) * 1000000000L + \
	(B).tv_nsec - (A).tv_nsec)
#define TIMESPEC2NS(T) ((uint64_t) (T).tv_sec * NSEC_PER_SEC + (T).tv_nsec)

/****************************************************************************/
#define BIT_VAL(data, bit_offs) ((data >> bit_offs) & 0x01)
#define ARR_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define IN_SECION(x, start, end) ((x >= start && x<=end) ? (1): (0)))


/******************************For Statistic Results***************************************/
#define OUTTER_COUNT_TIMES 10
#define INNER_COUNT_TIMES 100000
#define MAX_GLOBAL_LOOP_COUNT (OUTTER_COUNT_TIMES * INNER_COUNT_TIMES)
#define FIRST_STAGE_STATISTIC_ON 1     
#define SECND_STAGE_STATISTIC_ON 0     
#define GLOBAl_STATISTIC_ON      0    

//For statisc histgram
typedef struct Section {
	long start;
	long end;
}Section_T;

typedef struct HistNode {
	Section_T secntion;
	unsigned count;
	float percent;
}HistNode_T;


/*
	@description
		Just get max_val, min_val, ave_val from arr
	@param
		arr:
			The array to cal
		size:
			The size of array
		max_val,min_val,ave_val:
			The pointer to store results.
	@return
		NULL
*/
extern void ec_common_get_statistic_results(long arr[], long size, long *max_val, long *min_val, long *ave_val);

/*
	@description
		Calculate the histgram of arr.
		Each section in h[i].section.start~h[i].section.end, count times of data in h[i].section is h[i].count, percent is h[i].percent.
	@param
		arr:
			The array to calculate
		size:
			The size of array
		h:
			The array of histgram
		hist_size;
			The size of h
	@return
		NULL
*/
extern void ec_common_get_statistic_results_hist(long arr [],long size, HistNode_T *h, int hist_size);

/*
	@description
		Just set count=0,percent=0.0 for each element in h.
	@param
		h:
			The array of histgram
		hist_size;
			The size of h
	@return
		NULL
*/
extern void ec_common_reset_hist(HistNode_T *h, int hist_size);

/*
	@description
		Disp conetent of histgram of h.
	@param
		hist_name:
			The name to show of histgram.If NULL, name is set "StatisticHist".
		h:
			The array of histgram
		hist_size;
			The size of h
	@return
		NULL
*/
extern void ec_common_disp_hist(char *hist_name, HistNode_T *h, int hist_size);

/*
	@description
		Disp conetent of histgram of regs.
	@param
		regs:
			Array to display,end with {}
	@return
		NULL
*/
extern void ec_common_disp_ec_pdo_entry_reg_array(ec_pdo_entry_reg_t *regs);

/******************************For Generate for free reg data***************************************/
/*
	@description
		Generate array of ec_pdo_entry_t used by ecrt_domain_reg_pdo_entry_list.
		Waring:If return a not NULL pointer, the pointer must be free by call ec_common_free_pdo_entry_reg_arr. 
	@param
		alias:
			Slave alias
		postion:
			Slave position
		vendorid:
			Slave vendorid
		productid:
			Slave productid
		pdo_entries:
			Which generated by ethercat command tool (ethercat cstruct)
		pdo_entries_size:
			Size of pdo_entries	
	@return
		A pointer to ec_pdo_entry_reg_t if malloc success, else return NULL	
*/
extern ec_pdo_entry_reg_t *ec_common_generate_pdo_entry_reg_arr(int alias, int position, int vendorid, int productid, ec_pdo_entry_info_t pdo_entries[], int pdo_entries_size);

/*
	@description
		Free space for regs.
		Warn it free pointer self , and malloced space for offset and bit_position 
	@param
		regs:
			Base addr to free
	@return 
		None
*/
extern void ec_common_free_pdo_entry_reg_arr(ec_pdo_entry_reg_t *regs);


/******************************For init or configure slave specified info***************************************/

/*
	@description
		Just set all to zero.
	@param
		slave_specified_info:
			The base struct pointer to set. 
	@return 
		None
*/
extern void ec_common_reset_slave_specified_info(SlaveSpecifiedInfo_T *slave_specified_info);

/*
	@description
		To configure global slave id.
		In ethrecat network, slave can be found by vendorid,productcode,alias and position.
	@param
		  slave_specified_info,vendorid,productcode,alias,position
	@return 
		None
*/
extern void  ec_common_config_slave_specified_info_id(SlaveSpecifiedInfo_T *dst, SlaveIdInfo_T*src);

/*
	@description
		To configure global slave pdo_entries.
	@param
		  pdo_entries:
		      Generated by cmd 'ethercat cstruct', like slave_0_pdo_entries[], slave_1_pdo_entries[], ...
		  pdo_entries_size:
		      The size of pdo_entries
	@return 
		None
*/
extern void ec_common_config_slave_specified_info_pdo_entries(SlaveSpecifiedInfo_T *slave_specified_info, ec_pdo_entry_info_t *pdo_entries, unsigned int pdo_entries_size);

/*
	@description
		To configure global slave pdos.
	@param
		  pdo_entries:
		      Generated by cmd 'ethercat cstruct', like slave_0_pdos[], slave_1_pdos[], ...
		  pdo_entries_size:
		      The size of pdos
	@return
		None
*/
extern void ec_common_config_slave_specified_info_pdos(SlaveSpecifiedInfo_T *slave_specified_info, ec_pdo_info_t *pdos, unsigned int pdos_size);

/*
	@description
		To configure global slave syncs.
	@param
		  pdo_entries:
		      Generated by cmd 'ethercat cstruct', like slave_0_syncs[], slave_1_syncs[], ...
		  pdo_entries_size:
		      The size of syncs
		None
*/
extern void ec_common_config_slave_specified_info_syncs(SlaveSpecifiedInfo_T *slave_specified_info, ec_sync_info_t *syncs, unsigned int syns_size);

/*
	@description
		To configure global slave domain regs.
		slave_specified_info->domain_regs generate by ec_common_generate_pdo_entry_reg_arr
	@param
		  pdo_entries:
		      Generated by cmd 'ethercat cstruct', like slave_0_syncs[], slave_1_syncs[], ...
		  pdo_entries_size:
		      The size of syncs
	@return
		1 configure success
		0 configure failed
*/
extern int  ec_common_config_config_slave_specified_info_domain_regs(SlaveSpecifiedInfo_T *slave_specified_info);


/******************************For init or configure master specified info***************************************/

/*
	@description
		Just set all to zero.
	@param
		master_specified_info:
			The base struct pointer to set. 
	@return 
		None
*/
extern void ec_common_reset_master_specified_info(MasterSpecifiedInfo_T *master_specified_info);

/*
	@description
		To set some necessary data for master
	@param
		master_index:
			index of requested master
		slaves_count:
			How many slaves controled by master
		slaves_pecified_infos:
			All slaves info.
			Warn:The last element in array of slaves_pecified_infos:{vendorid, productcode, alias, position} = {-1, -1, -1, -1}
	@return 
		None
*/
extern void ec_common_config_master_specified_info(MasterSpecifiedInfo_T *master_specified_info, int master_index, int slaves_count, SlaveSpecifiedInfo_T *slaves_specified_infos);

/*
	@description
		Packge function to init all necessay steps before do other things.
		One MasterSpecifiedInfo_T store all doamin info, domain_pd info, slave_config info, slave_specified info... retlated one master.
		Usually we just to maintain one MasterSpecifiedInfo_T to use one master.
	@param
		master_specified_info			
	@return 
		ErrCode define in enum EcCommonMasterErr_T
*/
extern EcCommonErr_T ec_common_init_master_specified_info(MasterSpecifiedInfo_T *master_specified_info);

/*
	@description
		You must call this funciton before use master.
		This funuction will activate master, config domain_pd and domian_pd_sizes
	@param
		master_specified_info			
	@return 
		ErrCode define in enum EcCommonMasterErr_T
*/

extern EcCommonErr_T ec_common_activate_master(MasterSpecifiedInfo_T *master_specified_info);

/*
	@description
		When exit application.You should call ec_common_master_free to free malloced space of slave_configs and slave_specified_info
		It also call ecrt_release_master to free master.
		Warning:You must reconfigure your master_specified_info before you use it again.
	@param
		master_specified_info			
	@return 
		NULL
*/
extern void ec_common_master_free(MasterSpecifiedInfo_T *master_specified_info);

/*
	@description
		To disp all infomation attached to master.
	@param
		master_specified_info			
	@return 
		NULL
*/
extern void ec_common_traverse_master_slave_info(MasterSpecifiedInfo_T *master_specified_info);


/******************************For process pdo data***************************************/
/*
	@description
		Get pdo data form domain_pd.
	@param
		master_specified_info:
			Master to operate.
		slave_id_info:
			Which slave to get pdo data.
		index:
			Index of Object-Dictionary to get data.
		sub_index:
			Subinde of Object-Dictionary to get data.
		type:
			Which data of Object-Dictionary

		data:
			To sotre value of data.
			
	@return 
		ErrCode define in enum EcCommonMasterErr_T
			
*/
EcCommonErr_T ec_common_get_pdo_data(MasterSpecifiedInfo_T *master_specified_info, SlaveIdInfo_T*slave_id_info, unsigned int index, unsigned int sub_index, EcCommonDataType_T type, void *data);

/*
	@description
		Get pdo data to domain_pd.
	@param
		master_specified_info:
			Master to operate.
		slave_id_info:
			Which slave to get pdo data.
		index:
			Index of Object-Dictionary to get data.
		sub_index:
			Subinde of Object-Dictionary to get data.
		type:
			Which data of Object-Dictionary

		value:
			value to set	
	@return 
		ErrCode define in enum EcCommonMasterErr_T
*/
EcCommonErr_T ec_common_set_pdo_data(MasterSpecifiedInfo_T *master_specified_info, SlaveIdInfo_T*slave_id_info, unsigned int index, unsigned int sub_index, EcCommonDataType_T type, void *value);

/*
	@description
		Just compare two SlaveIdInfo_T.
	@param
		slave_info_dst,slave_info_src:
			Two data to compare
	@return 
		1:
			*slave_info_dst == *slave_info_src
		0:
			*slave_info_dst != *slave_info_src
*/
int ec_common_compare_slave_id_info(SlaveIdInfo_T *slave_info_dst, SlaveIdInfo_T *slave_info_src);


/*
	@description
		To laocte  slave(which id is slave_id_info) in array of master_specified_info->slaves_specified_infos[]
	@param
		master_specified_info,slave_id_info
			
	@return 
		-1:
			Slave (which id is *slave_id_info) not exist in array of master_specified_info->slaves_specified_infos[]
		idnex:
			If slave exist in array of master_specified_info->slaves_specified_infos[]
			reutrn index which master_specified_info->slaves_specified_infos[index].slave_id_info == *slave_id_info
*/
int ec_common_locate_slave_pos(MasterSpecifiedInfo_T *master_specified_info, SlaveIdInfo_T *slave_id_info);

/*
	@description
		To laocte  Object-Dictionary(index, sub_index) in array of domain_regs[]
	@param
		master_specified_info,slave_id_info
			
	@return 
		-1:
			Object-Dictionary not exist in array of domain_regs[]
		idnex:
			If Object-Dictionary exist in domain_regs[]
			reutrn index which 'domain_reg[index].index == index && domain_reg[index].subindex == subindex'
*/
int ec_common_locate_index_pos(ec_pdo_entry_reg_t *domain_regs, unsigned int index, unsigned int subindex);



/******************************For sequence excute sdo commands***************************************/
/*
	@description
		Package for ecrt_master_sdo_download
	@param
		master_specified_info,slave_id_info
		index, subindex, data, data_size, abort_code referened to ecrt_master_sdo_download
			
	@return 
		return ecrt_master_sdo_download
*/
int ec_common_sdo_download(MasterSpecifiedInfo_T *master_specified_info, SlaveIdInfo_T *slave_id_info, uint16_t index, uint8_t subindex, uint8_t *data, size_t data_size, uint32_t *abort_code);

/*
	@description
		Package for ecrt_master_sdo_upload
	@param
		master_specified_info,slave_id_info
		index, subindex, data, data_size, abort_code referened to ecrt_master_sdo_upload
		
			
	@return 
		return ecrt_master_sdo_upload
*/
int ec_common_sdo_upload(MasterSpecifiedInfo_T *master_specified_info, SlaveIdInfo_T *slave_id_info, uint16_t index, uint8_t subindex, uint8_t *data, size_t data_size, size_t *result_size, uint32_t *abort_code);

#endif //MY_TEST_ECAT_COMMON_H

#ifdef __cplusplus
}
#endif



